One of the current downsides with running in Windows Azure is that
you don’t get the usual set of tools to peek inside a Windows machine. You
currently can’t use remote desktop, nor can you log in to the box and run
a command shell.Or can you?
Setting up a remote command-line session using standard Windows
tools such as psexec or remote.exe isn’t possible, since all role
instances are behind a load balancer. These tools are not available,
except when you have a session open with the “server’” portion, and that
isn’t possible when every request could be directed to a different virtual
machine. Also, none of these tools tunnel over HTTP, which is the port
opened by default on Windows Azure.
To work around these limitations, let’s write some code that will
act as a frontend to the Windows command shell—cmd.exe. The idea here is simple. A web role
will be augmented with some simple UI to input commands and see command
output. Whenever a user enters a new command, the web role passes the
command to a new instance of cmd.exe,
executes it, and returns the output. The output is captured, and then
displayed on a web page, making the website into a
command-shell-proxy-thingamajig-whatchamacallit (that’s my official name
for it).
The magic that will make all this work is the enableNativeCodeExecution flag. Without that
flag, code running in the cloud won’t be able to perform full-trust
actions such as launching a new process. With that flag set, the
restriction no longer exists, and code can launch native processes
whenever it feels like it. Note that any process will still run under the
context/user account of the web role. Although native code can be run,
that code will still not have any administrator-level access to the
machine.
Warning: Since this is just a sample, there will be no authentication or
security built in. However, the danger of leaving this running can’t be
overstated. Any malicious user who gets his hands on this can
essentially do anything on your virtual machine that your code can. This
is a sure recipe for disaster. Do not run this code in
production without some form of protection.
1. Building the Command Shell Proxy
Let’s start by creating a blank CloudService and adding a web role to it. In
this sample, let’s call the cloud service NativeCodeCmd. (You can obviously choose any
name for it.) The Solution Explorer should look similar to Figure 1.
Users will need a place to type in commands and see the output of
their commands. This calls for some UI. In this case, the UI is going to
be quite Spartan (and downright ugly), but functional. There’ll be one
text box to type in commands, and then one more text box to show output.
To keep things simple, let’s use the ASP.NET controls that come out of
the box.
Change the markup inside the body tag in Default.aspx to match the code shown in Example 1.
Example 1. Two text boxes and a button
<body> <form id="form1" runat="server"> <div> Command: <asp:Textbox id="txtCmd" runat="server" /> <asp:Button id="btnRun" runat="server" OnClick="btnRun_Click" Text="Run" /> <br/><br/>
Output: <asp:Textbox TextMode="MultiLine" style="color:white;background-color:black;" id="txtOutput" runat="server" Height="413px" Width="890px" />
</div> </form> </body>
|
This is quite a simple ASP.NET markup. There are three different
UI elements here: a text box named txtCmd to accept commands, a multiline text
box named txtOutput to show command
output, and a button called btnRun to
run the command. btnRun has an event
handler defined to call the btnRun_Click method when pressed. When you run
it by pressing F5, the Dev Fabric should launch it, and your default
browser should open to something similar to what is shown in Figure 2.
This is not exactly a showcase of website design and aesthetics,
is it? Feel free to play with the design and make it less painful on the
eyes.
This doesn’t do anything yet, so let’s make it useful. The Windows
command shell cmd.exe has a
/c parameter that enables you to pass
commands as startup arguments to it. Cmd.exe will execute the command and print
the results to standard output (stdout). To make this command proxy work, the
button click handler must spin up cmd.exe with the /c parameter, pass the right command in, and
redirect standard output to the text box. Example 2 shows the code to do
this.
Example 2. Launching cmd.exe and capturing stdout
protected void btnRun_Click(object sender, EventArgs e) {
var newProc = new System.Diagnostics.Process();
// Fill in new process object's startinfo // cmd.exe will always exist in the path. Pass in // the /c parameter followed by the contents of the // txtCmd text box. Redirect standard output newProc.StartInfo.FileName = "cmd.exe"; newProc.StartInfo.Arguments = "/c" + txtCmd.Text; newProc.StartInfo.UseShellExecute = false; newProc.StartInfo.RedirectStandardOutput = true; newProc.EnableRaisingEvents = false;
newProc.Start();
// Capture standard output and append it to the multiline // text box txtOutput.Text += newProc.StandardOutput.ReadToEnd()+"\r\n";
newProc.Close();
}
|
The code shown in Example 2 uses the System.Diagnostics.Process class to spin up a
new instance of cmd.exe, pass
arguments, and read the output of the process before closing it.
Press F5 to run the website under the Dev Fabric. Type in a
command (say, dir) and press
Enter. Instead of a listing of files, the output is a security
exception, as shown in Figure 3.
Why is that? Remember that, on the sandbox, managed code in
Windows Azure runs in medium trust by default, which blocks creation of
processes programmatically. To do this, the security chains must be
loosened by enabling full trust/native code execution.
2. Enabling Native Code Execution
Enabling native code execution is easy. Open ServiceDefinition.csdef and modify the code
as shown in Example 3.
Only one change needs to be made: set enableNativeCodeExecution to true.
Example 3. Native code-enabled service definition
<?xml version="1.0" encoding="utf-8"?> <ServiceDefinition name="NativeCodeCmd" xmlns= "http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition"> <WebRole name="WebRole1" enableNativeCodeExecution="true"> <InputEndpoints> <InputEndpoint name="HttpIn" protocol="http" port="80" /> </InputEndpoints> <ConfigurationSettings /> </WebRole> </ServiceDefinition>
|
This attribute tells the Windows Azure fabric that this specific
role instance should be launched differently. When launched, all role
instances are placed in a specific kind of hosting environment. This
flag changes the hosting environment to run the application in full
trust, rather than the Windows Azure trust sandbox.